package com.util.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 对象封装
 *  * 基础Controller
 *  *封装代码参考：https://blog.csdn.net/qq_16605855/article/details/80618992
 *  https://download.csdn.net/download/goinni/4310703
 */
public class BaseController extends HttpServlet {
    private HttpServletRequest request;
    private HttpServletResponse response;


    /**
     * 方法逻辑 1.获取Controller类的Class对象
     * 2.根据接收的接口名称  获取Controller对应的接口文件不存在
     * 3.判断是有参接口还是无参接口，有参接口调用混合方法获取参数对象。
     * 4.执行接口。
     * 5.返回接口对象。
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        ServletContext context = getServletContext();
//        String ss  = context.getRealPath("/WEB-INF/application.yml");
//        System.out.println(ss);
        this.request = req;
        this.response = resp;
       Class controClass = this.getClass();
       String reqMethod = req.getParameter("method");
       if(reqMethod == null || reqMethod.length() == 0 ){
           throw new RuntimeException("未传入接口名称");
       }
       //接口方法
       Method method = null;
        try {
             method = this.getMethod(controClass,reqMethod);
            System.out.println(String.format("请求接口：%s",method.getName()));
        } catch (Exception e) {
          throw new RuntimeException(String.format("接口:%s获取失败:%s",reqMethod,e.getMessage()));
        }
        //接口参数对象
        Object[] parameter = null;

        if(method.getParameterCount()<=0){
            parameter = null;
        }else{
            parameter = this.getMixParame(method,req);
            for(Object o   :parameter){
                System.out.println("请求参数："+o.toString());
            }
        }

        //获取返回对象
        String result = null;

        try {
            method.setAccessible(true);//跳过语言查询
            result =    (String) method.invoke(controClass.newInstance(),parameter);

            System.out.println(String.format("获取返回参数:%s",result));
        } catch (Exception e) {

           throw new RuntimeException(String.format("接口:%s执行失败:%s",reqMethod,e.getMessage()));
        }
        //发送给前端
        PrintWriter out = resp.getWriter();

        try {

            out.write(result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            out.flush();
            out.close();
        }

    }


    /**
     * 获取接口方法
     * @param c
     * @param
     * @param way
     * @return
     * @throws Exception
     */
    public Method getMethod(Class c,String way) throws Exception{

        Method[] methods = c.getMethods();

        //获取方法
        Method ways = null;
        for(Method m : methods){
            if(way.equals(m.getName())){
                ways = m;
            }
        }
        if(ways == null){
            throw new RuntimeException(String.format("调用方法:%s不存在",way));

        }
        return ways;
    }
    /**
     * <h1>通过反射获取<code>Bean</code>对象属性值</h1>
     * 	<p>该方法将取得form 或 url 对应Bean 的参数值</p>
     * @return Object form bean 实类对象
     * @author <a href="mailto:hou.zhy@neusoft.com">侯振宇</a>
     */
    public <T>T getEntity(HttpServletRequest request,Parameter parameter) throws Exception{

        Object object = null; //返回的form bean 实类对象
        Object param=null; //接受为bean参数赋值的对象
        //获取接口方法

       // ways.setAccessible(true);
        //获取参数class对象
       // Class clazz = ways.getParameterTypes()[0];

        Class clazz = parameter.getType();
        try {
            object = clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(String.format("实体化实体对象:%s失败,失败原因:%s",clazz.getSimpleName(),e.getMessage()));
        }
        //获取实体成员
        Field[] fields = clazz.getDeclaredFields();

        try{
        if (fields.length > 0 ){
            for (Field field:fields){
                field.setAccessible(true);
                param = request.getParameter(field.getName().trim());
                if (param!= null
                        && !param .equals("")
                        && !param.equals(0)
                        && !param.equals("null")
                        && !param.equals("0")) {
                    //转换成int
                    if(field.getType().equals(int.class) || field.getType().equals(Integer.class)){
                        //这里不能强转，要用包装类包装 这里有点坑爹啊
                        field.set(object,Integer.parseInt((String)param));
                    }
                        //string 型
                    else if(field.getType().equals(String.class)) {
                        field.set(object, (String)param);
                    }
                    else{
                        field.set(object,param);
                    }
                }
            }
        }}catch (Exception e){
            System.out.println("包装实体参数失败："+e.getMessage());
            e.printStackTrace();
        }
        return (T) object;
    }

    /**
     * 封装请求参数
     * @param request
     * @param method
     * @return
     */
    public Object getParameter(HttpServletRequest request,Method method) throws Exception{

        //几组参数类型供比较
        Class string = String.class;
        Class int1 = java.lang.Integer.class;
        Class int2 = int.class;
        Class bool = Boolean.class;
        Class bool2 = boolean.class;
        //获取接口参数数组
        Parameter[] parameters = method.getParameters();
        //没有参数就返回
        if(parameters.length <=0){

            return null;
        }


        //封装对象
        Object[] objects = new Object[parameters.length];

        //遍历参数
        for(int i = 0;i<parameters.length;i++){
            //获取参数
            Parameter parameter = parameters[i];
            //获取参数类型
            Class paraType = parameter.getType();
            //参数健
            String paraName = parameter.getName();
            //参数值
            String paraValue = request.getParameter(paraName);

            //String 类型
            if(paraType.equals(string)){
                objects[i] = request.getParameter(paraName);
            }
            //int型
            else if(paraType.equals(int1) || paraType.equals(int2)){
                objects[i] = Integer.parseInt(request.getParameter(paraName));
            }
            //bool 型
            else if(paraType.equals(bool) || paraType.equals(bool2)){
                objects[i] = Boolean.valueOf(request.getParameter(paraName));
            }
            //更多类型
            //else if()
        }
         return objects;
    }

    /**
     * 根据接口参数判断调用哪个方法
     * 封装成实体对象 还是参数对象
     * 1:无参数;2:实体对象;3:参数对象
     * 判断逻辑：判断方法首参与请求参数是否有匹配，有就是参数对象，无就是实体对象
     * 此逻辑只能适用单个对象，不能适用多个对象
     * @param parameters
     * @return
     */
    private int boolParam(HttpServletRequest request,Parameter[] parameters){
        //默认为无参
        int flag = 1;
        //无参数
        if(parameters.length <=0){
            return flag;
        }
        String para = parameters[0].getName();
        String paraName = request.getParameter(para);
        //实体对象
        if(paraName.length() ==0 || paraName == null){
            flag = 2;
        }
        //参数对象
        else {

            flag = 3;
        }
        return flag;
    }

    /**
     * 获取接口获取接口参数数组
     * @param method
     * @return
     */
    private Parameter[] getParameter(Method method){
        return  method.getParameters();
    }

    /**
     * 混合参数封装
     * @return
     */
    private Object[] getMixParame(Method method,HttpServletRequest request) {


        //几组参数类型供比较
        Class string = String.class;
        Class int1 = java.lang.Integer.class;
        Class int2 = int.class;
        Class bool = Boolean.class;
        Class bool2 = boolean.class;
        //获取接口参数数组
        Parameter[] parameters = method.getParameters();
        //没有参数就返回
        if(parameters.length <=0){

            return null;
        }


        //封装对象
        Object[] objects = new Object[parameters.length];

        //遍历参数
        for(int i = 0;i<parameters.length;i++){
            //获取参数
            Parameter parameter = parameters[i];
            //获取参数类型
            Class paraType = parameter.getType();
            //参数健
            String paraName = parameter.getName();
            //参数值
            String paraValue = request.getParameter(paraName);

            //String 类型
            if(paraType.equals(string)){
                objects[i] = paraValue;
            }
            //int型
            else if(paraType.equals(int1) || paraType.equals(int2)){
                objects[i] = Integer.parseInt(paraValue);
            }
            //bool 型
            else if(paraType.equals(bool) || paraType.equals(bool2)){
                objects[i] = Boolean.valueOf(paraValue);
            }
            //实体数据
            else{
                try {
                    //修复传递多参数异常的问题
                    objects[i] = this.getEntity(request,parameter);
                } catch (Exception e) {
                    System.out.println(String.format("实体参数封装异常:%s /n异常信息:%s",paraName,e.getMessage()));
                    e.printStackTrace();
                }
            }
        }
        return objects;
    }

    public HttpServletRequest getRequest() {
        return this.request;
    }

    public HttpServletResponse getResponse() {
        return this.response;
    }
}
